/*******************************************************************************
 * @file    pm.c
 * @author  roger.luo
 * @date    2019-04-19
 * @brief   电源管理
 *
 *
 * @attention
 *
 ******************************************************************************/
#include <stdio.h>

#include "stm32u5xx_ll_pwr.h"
#define LOG_TAG TAG_LP
#include "elog.h"

#include "task/comm/ota.h"
#include "task/misc/rtc_app.h"
#include "task/service/include/swei_login.h"
#include "bsp/inc/bsp_modem.h"
#include "task_gnss.h"
#include "sensor/skeleton/sensor_support_chip.h"
#include "misc/misc_system.h"
#include "bsp_gsensor.h"
#include "bsp_gnss.h"
#include "comm/co_heartbeat.h"
#include "hal_adapter.h"
#include "bsp_comm.h"
#include "bsp_eflash.h"
#include "bsp.h"
#include "bsp_wdt.h"
#include "service/include/swei_location.h"
#include "misc/misc_system.h"
#include "bsp_uart.h"
#include "bsp_debug.h"
#include "hal_adapter.h"

#include "gnss_public.h"

#include "include/low_power.h"
#include "include/lp_comcu.h"
#include "include/lp_dqa.h"
#include "include/lp_gnss.h"
#include "include/lp_gsensor.h"
#include "include/lp_modem.h"
#include "include/lp_ota.h"
#include "include/lp_report.h"

#include "misc/task_misc.h"

#define LOWPOWER_DETECT_INTERVAL (1*1000)

/**
 * @brief 唤醒源
 */
const wakeup_name_t wakeup_name[] = {
    {WAKEUP_BY_COMCU,   "CoMCU"     },
    {WAKEUP_BY_MODEM,   "Modem"     },
    {WAKEUP_BY_GSENSOR, "Gsensor"   },
    {WAKEUP_BY_RTC,     "RTC"       },
};

/**
 * @brief 低功耗记录
 */
static pm_t pm = {0};

/**
 * @brief 获取唤醒源
 * @return unsigned int
 */
wakeup_source_e lp_wakeup_type_get(void)
{
    return pm.wakeup_type;
}

/**
 * @brief 设置唤醒源
 * @param  wakeup_type      My Param doc
 */
void lp_wakeup_type_set(wakeup_source_e wakeup_type)
{
    pm.wakeup_type |= wakeup_type;
}

bool lp_wakeup_type_check(wakeup_source_e wakeup_type)
{
    return (pm.wakeup_type & wakeup_type) ? true : false;
}

void lp_wakeup_type_clear(wakeup_source_e wakeup_type)
{
    pm.wakeup_type &= ~wakeup_type;
}

/* 报文休眠策略(20241120与客户沟通) */
/*
                            激活                                                                  非激活
正常(COMCU开机)             15s心跳 / 30s整车数据上报 / 10s gps/lbs/(wifi) /300s rsrp             15s心跳 / 前2min 每10s gps/lbs/(wifi)
低功耗(COMCU关机)           3min心跳/2hgps/lbs/(wifi)/2h rsrp/tbox小电池电量                      shutdown

*/

extern sleep_item_t lp_report;
extern sleep_item_t lp_modem;
extern sleep_item_t lp_gnss;
extern sleep_item_t lp_ota;
extern sleep_item_t lp_gsensor;
extern sleep_item_t lp_comcu;
extern sleep_item_t lp_dqa;
extern sleep_item_t lp_shell;
extern sleep_item_t lp_misc;
/* 系统进入低功耗投票项 */
sleep_item_t *sleep_item[] = {
    &lp_report,
    &lp_modem,
    &lp_gnss,
    &lp_gsensor,
    &lp_comcu,
    &lp_ota,
    &lp_dqa,
    &lp_shell,
    &lp_misc,
};

static uint32_t wakeup_work_time_start = 0;
static uint32_t lp_slp_msec = 0;
uint32_t sleep_item_count = ARRAY_COUNT(sleep_item);

/*
 * @brief       休眠检测
 * @param[in]   none
 * @return      uin32_t
 */
uint32_t sleep_check(void)
{
    unsigned int total_sleeptime    = TASK_ALWAYS_SLEEP;
    unsigned int interval           = 0;                        /* 最大休眠时间 */

    /* 扫描所有任务,执行睡眠前处理 */
    for(int i = 0; i < sleep_item_count; i++) {

        if (sleep_item[i]->canstop != NULL) {
            interval = sleep_item[i]->canstop();

            /* 有某个任务不允许休眠, 则不需要继续判断 */
            if (0 == interval) {
                total_sleeptime = 0;
                break;
            }

            if (interval < total_sleeptime)
                total_sleeptime = interval;
        }
    }

    /* 过小的休眠间隔没必要进入休眠(设置为0) */
    if (total_sleeptime < MIN_SLEEP_VOTE_TIME)
        total_sleeptime = 0;

    return total_sleeptime;
}

/**
 * @brief 获取休眠计时
 */
unsigned int lowpower_sleep_sec_get(void)
{
     return lp_slp_msec / 1000;
}

/**
 * @brief 休眠进入前处理所有引脚配置
 */
static void lowpower_enter_bsp_process(void)
{

}

/**
 * @brief 休眠退出后恢复所有引脚配置
 */
static void lowpower_exit_bsp_process(void)
{

}

/**
 * @brief 休眠函数
 */
void lowpower_loop(uint32_t total_sleeptime)
{
    int tmp_total_sleeptime = 0;

    /* 挂起所有任务, 避免定时唤醒任务调度 */
    vTaskSuspendAll();

    lowpower_enter_bsp_process();

    /* 在判断一次休眠时长, 避免之前产生调度, 休眠时长发生变化 */
    if (0 == sleep_check())
        goto leave;

    if (!lowpwr_prestop_init_subsec())
        goto leave;

    tmp_total_sleeptime = total_sleeptime;
    while (tmp_total_sleeptime > 0) {
        lp_slp_msec = tmp_total_sleeptime > MAX_SLEEP_TIME ? MAX_SLEEP_TIME : tmp_total_sleeptime;

        __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, 0);

        /*RTCCLK = 32kHZ, WakeUpTimer = 32/16 = 2KHZ, tick_ms= ms*2 */
        HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, lp_slp_msec * 2, RTC_WAKEUPCLOCK_RTCCLK_DIV16, 0);

        /* 睡前先喂狗 */
        bsp_wdt_feed();

        bsp_enter_lowpower();

        /* 醒来先喂狗 */
        bsp_wdt_feed();

        /* 其他中断类型退出 */
        if (WAKEUP_BY_NONE != pm.wakeup_type && pm.wakeup_type != WAKEUP_BY_RTC) {
            break;
        }

        tmp_total_sleeptime -= lp_slp_msec;
    }

    lowpwr_poststop_exit_subsec();

leave:
    lowpower_exit_bsp_process();
    xTaskResumeAll();
}

/**
 * @brief 进入低功耗处理
 * @return
 */
void enter_lowpower(uint32_t total_sleeptime)
{
    /* 打印休眠时间 */
    log_i("try to sleep time=%d.\r\n", total_sleeptime);
    tx_buf_flush(debug_uart_id);

    /* 不再打印log */
    bsp_debug_uart_sleep();

    /* 休眠前处理 */
    for (int i = 0; i < sleep_item_count; i++) {
        if (NULL != sleep_item[i]->prestop)
            sleep_item[i]->prestop();
    }

    pm.wakeup_type = WUP_SRC_NONE;
    /* DO NOT MOVE!!! */
    lowpower_loop(total_sleeptime);
    bsp_debug_uart_wakeup();
}

/*
 * @brief       退出低功耗处理
 * @param[in]   ms -
 * @return      none
 */
void exit_lowpower(void)
{
    /* 非RTC唤醒则清除, 避免打印RTC唤醒 */
    if (WUP_SRC_RTC != pm.wakeup_type) {
        lp_wakeup_type_clear(WUP_SRC_RTC);
    }

    /* 执行唤醒后操作 */
    for(uint32_t i = 0; i < sleep_item_count; i++) {
        if (sleep_item[i]->poststop != NULL)
            sleep_item[i]->poststop();
    }

    for (uint8_t i = 0; i < sizeof(wakeup_name) / sizeof(wakeup_name_t); i++) {
        if (pm.wakeup_type & wakeup_name[i].type) {
            log_i("device wakeup by [%s]\r\n", wakeup_name[i].name);
            break;
        }
    }
}

/*
 * @brief       电源管理任务
 * @param[in]   none
 * @return      none
 */
void task_lowpower_process(void)
{
    uint32_t total_sleeptime = 0;

    /* 轮询检查间隔 */
    if (!timeout_ms(wakeup_work_time_start, LOWPOWER_DETECT_INTERVAL))
        return;
    wakeup_work_time_start = getms();

    /* 时间未校时成功, 不允许休眠, 避免休眠时间跳变 */
    if (0 == get_time_sync_flg())
        return;

    total_sleeptime = sleep_check();
    /* 不能休眠 */
    if (0 == total_sleeptime)
        return;

    /* 进入休眠 */
    enter_lowpower(total_sleeptime);

    /* 退出休眠 */
    exit_lowpower();

    wakeup_work_time_start = getms();
}
